﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.DirectoryServices;
using System.Reflection;
using ActiveDs;
using System.Security.Principal;
using System.DirectoryServices.ActiveDirectory;

namespace ViVa.ActiveDirectory
{
    public class ActiveDirectory : IDisposable
    {
        #region Constructor
        public ActiveDirectory(string Query, string UserName, string Password, string Path)
        {
            Entry = new DirectoryEntry();
            this.Path = Path;
            this.UserName = UserName;
            this.Password = Password;
            this.Query = Query;
            Searcher = new DirectorySearcher(Entry);
            Searcher.PageSize = 1000;
            Searcher.Filter = Query;
        }
        #endregion
        
        
        #region User Functions
        public List<ADEntry> GetUserList(string Filter)
        {
            Filter = string.Format("(&({0}))", Filter);
            Searcher.Filter = Filter;
            return FindAllUsers();
        }
        
        public DirectoryEntry GetUserDirectoryEntry(ADEntry adEntry)
        {
            string Filter = string.Format(UserSamAccountNameFilter, adEntry.SamAccountName);
            Searcher.Filter = Filter;
            SearchResult Result = Searcher.FindOne();
            return Result.GetDirectoryEntry();
        }

        public void ChangeUserPassword(ADEntry adEntry, string passWord)
        {
            string Filter = string.Format(UserSamAccountNameFilter, adEntry.SamAccountName);
            var directoryEntry = this.GetUserDirectoryEntry(adEntry);
            directoryEntry.Invoke("SetPassword", passWord);
            directoryEntry.Dispose();
        }

        public void Save(ADEntry adEntry, bool SaveIfEmpty = false)
        {
            var directoryEntry = this.GetUserDirectoryEntry(adEntry);

            if (adEntry.Title != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Title", adEntry.Title);
            }

            if (adEntry.TelephoneNumber != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "TelephoneNumber", adEntry.TelephoneNumber);
            }

            if (adEntry.EmployeeID != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "EmployeeID", adEntry.EmployeeID);
            }

            if (adEntry.FacsimileTelephoneNumber != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "FacsimileTelephoneNumber", adEntry.FacsimileTelephoneNumber);
            }

            if (adEntry.Mobile != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Mobile", adEntry.Mobile);
            }

            if (adEntry.Pager != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Pager", adEntry.Pager);
            }

            if (adEntry.HomePhone != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "HomePhone", adEntry.HomePhone);
            }

            if (adEntry.Info != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Info", adEntry.Info);
            }

            if (adEntry.StreetAddress != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "StreetAddress", adEntry.StreetAddress);
            }

            if (adEntry.PostalCode != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "PostalCode", adEntry.PostalCode);
            }

            if (adEntry.l != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "l", adEntry.l);
            }

            if (adEntry.Department != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Department", adEntry.Department);
            }

            if (adEntry.PhysicalDeliveryOfficeName != null || SaveIfEmpty == true)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "PhysicalDeliveryOfficeName", adEntry.PhysicalDeliveryOfficeName);
            }

            if (adEntry.co != null)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "co", adEntry.co);
            }

            if (adEntry.Company != null)
            {
                Helper.SetValueFromDirectoryEntry(directoryEntry, "Company", adEntry.Company);
            }
            
            if (adEntry.AccountExpires != null)
            {
                // Voor wanneer account niet verloopt
                if (adEntry.AccountExpires == "0")
                {
                    Helper.SetValueFromDirectoryEntry(directoryEntry, "AccountExpires", 0);
                }
                // Zet anders de datum wanneer het account verloopt. De 22:00:00uur staat ingevult omdat anders één dag eerder wordt doorgegeven.
                else
                {
                    Helper.SetValueFromDirectoryEntry(directoryEntry, "AccountExpires", Helper.GetLargeIntegerFromDateTime(Helper.GetDateTimeFromString(adEntry.AccountExpires + " 22:00:00")));
                }
            }

            if (adEntry.AccountDisabled != null)
            {
                int val = (int)Helper.GetValueFromDirectoryEntry(directoryEntry, "userAccountControl");

                if (adEntry.AccountDisabled == false)
                {
                    Helper.SetValueFromDirectoryEntry(directoryEntry, "userAccountControl", val & ~0x2);
                }

                if (adEntry.AccountDisabled == true)
                {
                    Helper.SetValueFromDirectoryEntry(directoryEntry, "userAccountControl", val | 0x2);
                }
            }



            //  ***** Om door de properties te loopen. ******
            //foreach(PropertyInfo p in adEntry.GetType().GetProperties())
            //{
            //    if (p.Name == "AccountExpires")
            //    {
            //        Helper.SetValueFromDirectoryEntry(directoryEntry, p.Name, Helper.GetLargeIntegerFromDateTime(Helper.GetDateTimeFromString(adEntry.AccountExpires)));
            //    }

            //    else if (adEntry.GetType().GetProperty(p.Name).GetValue(adEntry, new object[] { }) != null)
            //    {
            //        Helper.SetValueFromDirectoryEntry(directoryEntry, p.Name, adEntry.GetType().GetProperty(p.Name).GetValue(adEntry, new object[] { }));
            //    }
            //}

            directoryEntry.CommitChanges();
            directoryEntry.Dispose();
        }

        private List<ADEntry> FindAllUsers()
        {
            List<ADEntry> ReturnResults = new List<ADEntry>();
            SearchResultCollection Results = Searcher.FindAll();
            foreach (SearchResult Result in Results)
            {
                var directoryEntry = Result.GetDirectoryEntry();
                var entry = new ADEntry()
                {
                    Name = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "name"),
                    SamAccountName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "samaccountname"),
                    Email = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "mail"),
                    GivenName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "GivenName"),
                    SN = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "SN"),
                    DistinguishedName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "DistinguishedName"),
                    Company = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Company"),
                    DisplayName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "DisplayName"),
                    Initials = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Initials"),
                    Title = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Title"),
                    CN = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "CN"),
                    Office = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Office"),
                    TelephoneNumber = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "TelephoneNumber"),
                    EmployeeID = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "EmployeeID"),
                    FacsimileTelephoneNumber = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "FacsimileTelephoneNumber"),
                    Mobile = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Mobile"),
                    Pager = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Pager"),
                    HomePhone = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "HomePhone"),
                    Info = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Info"),
                    StreetAddress = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "StreetAddress"),
                    PostalCode = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "PostalCode"),
                    l = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "l"),
                    co = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "co"),
                    Department = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Department"),
                    PhysicalDeliveryOfficeName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "PhysicalDeliveryOfficeName"),
                    Description = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "Description")
                }; 
                
                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "MemberOf") != null)
                {
                    List<string> Values = new List<string>();
                    PropertyValueCollection Collection = directoryEntry.Properties["memberof"];
                    foreach (object Item in Collection)
                    {
                        Values.Add((string)Item);
                    }
                    // Sort membership list
                    Values.Sort();
                    entry.MemberOf = Values;

                    //Onderstaand geeft een fout wanneer een user maar lid is van één groep (naast de standaard 'groep' "Domain Users")..
                    //entry.MemberOf = ((object[])(Helper.GetValueFromDirectoryEntry(directoryEntry, "memberof"))).Select(i => i.ToString()).ToList();
                    //entry.MemberOf = memberof;
                }
                else
                {
                    entry.MemberOf = null;
                }

                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "AccountExpires") != null)
                {
                    IADsLargeInteger largeInteger = (IADsLargeInteger)Helper.GetValueFromDirectoryEntry(directoryEntry, "AccountExpires");
                    if (largeInteger.LowPart != 0 && largeInteger.LowPart != -1)
                    {
                        entry.AccountExpires = Helper.GetStringFromDateTime(Helper.GetDateTimeFromLargeInteger(largeInteger));
                    }
                    else
                    {
                        entry.AccountExpires = "0";
                    }
                }

                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "userAccountControl") != null)
                {
                    int flags = (int)directoryEntry.Properties["userAccountControl"].Value;

                    if (!Convert.ToBoolean(flags & 0x0002))
                    { 
                        entry.AccountDisabled = false; 
                    }
                    else
                    {
                        entry.AccountDisabled = true;
                    }
                }

                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "objectSID") != null)
                {
                    entry.objectSID = new SecurityIdentifier((byte[])Helper.GetValueFromDirectoryEntry(directoryEntry, "objectSID"), 0).ToString();
                }

                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "LastLogonTimeStamp") != null)
                {
                    entry.LastLogonTimeStamp = Helper.GetDateTimeFromLargeInteger((IADsLargeInteger)Helper.GetValueFromDirectoryEntry(directoryEntry, "LastLogonTimeStamp"));
                }
                
                ReturnResults.Add(entry);
            }
            Results.Dispose();
            return ReturnResults;
        }

        //public List<ADEntry> FindAllUsers()
        //{
        //    List<ADEntry> ReturnedResults = new List<ADEntry>();
        //    SearchResultCollection Results = Searcher.FindAllUsers();
        //    foreach (SearchResult Result in Results)
        //    {
        //        ReturnedResults.Add(new ADEntry(Result.GetUserDirectoryEntry()));
        //    }
        //    Results.Dispose();
        //    return ReturnedResults;
        //}



        #endregion


        #region Group Functions

        public List<ADGroup> GetGroupList(string Filter, bool getMembers = true)
        {
            Filter = string.Format("(&({0}))", Filter);
            Searcher.Filter = Filter;
            return FindAllGroups(getMembers);
        }


        private List<ADGroup> FindAllGroups(bool getMembers = true)
        {
            List<ADGroup> ReturnResults = new List<ADGroup>();

            Searcher.Sort.PropertyName = "Name";
            Searcher.Sort.Direction = SortDirection.Ascending;

            SearchResultCollection Results = Searcher.FindAll();
            foreach (SearchResult Result in Results)
            {
                var directoryEntry = Result.GetDirectoryEntry();
                var group = new ADGroup()

                {
                    Name = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "name"),
                    DistinguishedName = (string)Helper.GetValueFromDirectoryEntry(directoryEntry, "DistinguishedName")
                };

                if (getMembers == true)
                {
                    if (Helper.GetValueFromDirectoryEntry(directoryEntry, "Member") != null)
                    {
                        List<ADEntry> Values = new List<ADEntry>();
                        PropertyValueCollection Collection = directoryEntry.Properties["Member"];
                        foreach (object Item in Collection)
                        {
                            Values.Add(ADService.GetUserByDistinguishedName((string)Item));
                        }
                        // Sort on Name
                        group.Member = Values.OrderBy(x => x.Name).ToList();
                    }
                }

                if (Helper.GetValueFromDirectoryEntry(directoryEntry, "objectSID") != null)
                {
                    group.objectSID = new SecurityIdentifier((byte[])Helper.GetValueFromDirectoryEntry(directoryEntry, "objectSID"), 0).ToString();
                }

                
                ReturnResults.Add(group);
            }
            return ReturnResults;
        }

        public void RemoveUserFromGroup(ADEntry userObj, ADGroup groupObj)
        {
            //Check if user is member of the group (For refresh of browser).
            if(groupObj.Member.Any(u => u.DistinguishedName.Contains(userObj.DistinguishedName)))
            {
            var directoryEntry = GetGroupDirectoryEntry(groupObj.DistinguishedName);
            directoryEntry.Properties["member"].Remove(userObj.DistinguishedName);
            directoryEntry.CommitChanges();
            directoryEntry.Dispose();
            }
            
        }

        public bool isUserMemberof(ADEntry userObj, ADGroup groupObj)
        {
            if (groupObj != null)
            {
                if (groupObj.Member != null)
                {
                    if (groupObj.Member.Any(u => u.DistinguishedName.Contains(userObj.DistinguishedName)))
                    {
                        return true;
                    }
                    else return false;
                }
                else return false;
            }
            else return false;
        }

        public void AddUserToGroup(ADEntry userObj, ADGroup groupObj)
        {
            if (groupObj != null)
            {
                var directoryEntry = GetGroupDirectoryEntry(groupObj.DistinguishedName);
                directoryEntry.Properties["member"].Add(userObj.DistinguishedName);
                directoryEntry.CommitChanges();
                directoryEntry.Dispose();
            }
        }

        public DirectoryEntry GetGroupDirectoryEntry(string groupDN)
        {
            string Filter = string.Format(GroupDNFilter, groupDN);
            Searcher.Filter = Filter;
            SearchResult Result = Searcher.FindOne();
            return Result.GetDirectoryEntry();
        }

        public static DateTime lastLogin(string samAccountName)
        {
            DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, "zorg.lan");
            DomainControllerCollection dcc = DomainController.FindAll(context);
            DateTime latestLogon = DateTime.MinValue;
            string servername = null;

            foreach (DomainController dc in dcc)
            {
                DirectorySearcher ds;
                using (dc)
                using (ds = dc.GetDirectorySearcher())
                {
                    ds.Filter = String.Format("(sAMAccountName={0})", samAccountName);
                    ds.PropertiesToLoad.Add("lastLogon");
                    ds.SizeLimit = 1;

                    SearchResult sr = ds.FindOne();

                    if (sr != null)
                    {
                        DateTime lastLogon = DateTime.MinValue;
                        if (sr.Properties.Contains("lastLogon"))
                        {
                            lastLogon = DateTime.FromFileTime((long)sr.Properties["lastLogon"][0]);
                        }

                        if (DateTime.Compare(lastLogon, latestLogon) > 0)
                        {
                            latestLogon = lastLogon;
                            servername = dc.Name;
                        }
                    }
                }
            }
            return latestLogon;
        }

        #endregion




        #region Properties
        public string Path
        {
            get { return _Path; }
            set
            {
                _Path = value;
                if (Entry != null)
                {
                    Entry.Close();
                }
                Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
                Searcher = new DirectorySearcher(Entry);
                Searcher.Filter = Query;
                Searcher.PageSize = 1000;
            }
        }
        public string UserName
        {
            get { return _UserName; }
            set
            {
                _UserName = value;
                if (Entry != null)
                {
                    Entry.Close();
                }
                Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
                Searcher = new DirectorySearcher(Entry);
                Searcher.Filter = Query;
                Searcher.PageSize = 1000;
            }
        }
        public string Password
        {
            get { return _Password; }
            set
            {
                _Password = value;
                if (Entry != null)
                {
                    Entry.Close();
                }
                Entry = new DirectoryEntry(_Path, _UserName, _Password, AuthenticationTypes.Secure);
                Searcher = new DirectorySearcher(Entry);
                Searcher.Filter = Query;
                Searcher.PageSize = 1000;
            }
        }
        public string Query
        {
            get { return _Query; }
            set
            {
                _Query = value;
                Searcher.Filter = _Query;
            }
        }
        public string SortBy
        {
            get { return _SortBy; }
            set
            {
                _SortBy = value;
                Searcher.Sort.PropertyName = _SortBy;
                Searcher.Sort.Direction = SortDirection.Ascending;
            }
        }
        #endregion



        #region Private Variables
        private DirectoryEntry Entry = null;
        private DirectorySearcher Searcher = null;
        private string _Path = "";
        private string _UserName = "";
        private string _Password = "";
        private string _Query = "";
        private string _SortBy = "";
        private string UserSamAccountNameFilter = "(&(objectClass=User)(objectCategory=Person)(SamAccountName={0}))";
        private string GroupDNFilter = "(&(objectClass=Group)(objectCategory=Group)(DistinguishedName={0}))";
        #endregion




        #region IDisposable Members
        public void Dispose()
        {
            if (Entry != null)
            {
                Entry.Dispose();
                Entry = null;
            }
            if (Searcher != null)
            {
                Searcher.Dispose();
                Searcher = null;
            }
        }
        #endregion

    }
}